本篇要示範的是,Go語言運用套件的重要性,並說明如何讓程式更好維護、更有組織等,在這個主題之下將理解,套件如何定義、套件內名稱匯出方式、替套件建立別名、Go Modules概念及Go get 匯入自訂或第三方套件。
在程式設計中,套件 (或 package) 指的是一組功能相關的程式碼,被組織在一起,以便於模組化、重用和維護。
在Go中,套件是多個 .go 檔案的集合,這些檔案位於相同的目錄下並有一個共同的 package 定義。Go的套件機制提供了程式碼的模組化和封裝。
套件的好處,在於開發出 好維護、可重複利用、模組化。
Don't Repeat yourself, DRY
Go 語言遵循 DRY 法則,即不應重複相同的程式碼。DRY 的初衷是將程式碼重構成函式,但當我們有成千上萬的函式該怎麼辦?
Go 語言允許讓你好幾個檔案共用一個套件,意思就是可以將一個套件拆成好幾個更好維護的小單元,而每個檔案都應該以該單元的功能來命名,Go語言並不在意套件中含有多少個檔案,只要同一個資料夾中的檔案都宣告為某某套件,他們就可以透過該套件存取。
如下,我們有一個string目錄,目錄底下又有多個 .go 檔案。
strings
builder.go
compare.go
reader.go
replace.go
search.go
string.go
...
名稱代表這個套件的內容、用途,十分重要。
應該符合:
例如 :
strconv (string conversion, 字串轉換)
regexp (regular expression, 正規表達式)
sync (synchronization, 同步運算)
os (operation system, 作業系統)
就是我們很常見到的 :
package <套件名稱>
就上述所說strings套件,當中所有檔案都是以 package strings 開頭,代表他們屬於strings套件。
在Go語言中,只有被匯出(exported)的套件變數、常數、型別、函式等能被套件外的程式存取;未匯出(unexported)的只限套件內使用。
如何判斷有無匯出 ? 很簡單
以英文大寫字母開頭,的東西就是可以被外部訪問,反之就不行,Go沒有所謂存取修飾符(access modifier)
例如: Java 中的 private 和 public 關鍵字。
對此,我們應該只匯出必要功能,不必要的應該隱藏起來。
補充說明 :
字首大寫,更精確地來說應該是 Unicode 大寫字元,來判斷該功能有沒有匯出。
這邊會說明另一個重要的觀念----理解 Go 編譯器會去哪裡尋找我們應用程式所引用的套件。
ps. 1.11版後,Go Modules 取代了$GOPATH
Go編譯器必須知道怎麼找到我們引用 import 匯入的套件的原始檔,編譯器會利用環境變數 $GOROOT 去尋找他們,所謂 $GOROOT 就是你 Go 在你電腦的安裝路徑,若要檢視可以輸入指令: go env
。
會看到一連串環境變數 :
$GOPATH 通常指向使用者的家目錄(home directory),例如在 Windows 中可能是 "C:\Users\使用者名稱\go"。而 $GOPATH 下會有三個子目錄:bin、pkg 和 src。
Go Modules 是為了取代 GOPATH 而生,其中 "modules" 代表了一系列的套件集合。透過模組路徑 (modules path) ,Go 能確切地找到你的套件。,這麼一來就不用依賴GOPATH來放置套件 :
如果你有使用到自訂或是外部套件,你必須再專案的根目錄建立一個 go.mod 檔案,辦法是輸入以下指令:
go mod init <模組名稱>
至於模組名稱,他不需要跟專案名稱或是專案資料夾同名,而且實際上可能寫成像是(example.com/mymodule)之形式。
當我們對程式加入或是移除套件時,我們應該要在專案目錄下輸入以下指令,好重整go.mod的內容:
go mod tidy
Go Modules 其實不只能用來尋找套件,它也能用於套件的版本控管 官方文件
我們可以使用 GO111MODULE 環境變數 (和GOROOT、GOPATH一樣可以透過 go env 觀看) 來控制 Go Module 有沒有啟用 :
專案名為Project1,即將打造的目錄結構 :
package shape
import (
"fmt"
)
type Shape interface {
area() float64
name() string
}
type Triangle struct {
Base float64
Height float64
}
type Rectangle struct {
Length float64
Width float64
}
type Square struct {
Side float64
}
func PrintShapeDetails(shapes ...Shape) {
for _, item := range shapes {
fmt.Printf("%s的面積: %.2f\n", item.name(), item.area())
}
}
func (t Triangle) area() float64 {
return (t.Base * t.Height) / 2
}
func (t Triangle) name() string {
return "三角形"
}
func (t Rectangle) area() float64 {
return t.Length * t.Width
}
func (t Rectangle) name() string {
return "長方形"
}
func (t Square) area() float64 {
return t.Side * t.Side
}
func (t Square) name() string {
return "正方形"
}
會提示你要不要用 go mod tidy 重整 go.mod
現在,資料夾 project01 就會出現一個 go.mod 檔案。
package main
import (
"project/src/project01/shape"
)
func main() {
t := shape.Triangle{Base:18.2, Height:30.2}
r := shape.Rectangle{Length: 20, Width: 23.3}
s := shape.Square{Side: 100}
shape.PrintShapeDetails(t, r, s)
}
輸出結果 :
接下來要將main編譯成一個可執行的二進位檔(要在/main的目錄),
步驟也就是將檔案產生二進制檔--->編譯為可執行文件exe檔--->運行exe。
此時我們可以看到該目錄下多了一個exe檔:
執行這個exe檔 :
當然也可以直接用指令 :
go run main.go
會比上面的慢。但原理一樣,轉為二進制寫在go run
底層在執行。
以上就是 golang 套件 Part 1 有關定義、匯出、組織結構的介紹,接下來是go get 取得與使用第三方模組或套件。